# -*- coding: utf-8 -*-
##############################################################################
#
#    OpenERP, Open Source Management Solution
#    Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
#
#    This program is free software: you can redistribute it and/or modify
#    it under the terms of the GNU Affero General Public License as
#    published by the Free Software Foundation, either version 3 of the
#    License, or (at your option) any later version.
#
#    This program is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU Affero General Public License for more details.
#
#    You should have received a copy of the GNU Affero General Public License
#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################

from openerp.osv import fields, osv
from openerp.tools.translate import _
from openerp import netsvc
from openerp.tools.float_utils import float_compare
#----------------------------------------------------------
# Purchase Order
#----------------------------------------------------------

class purchase_order(osv.osv):

    _inherit = "purchase.order"

    STATE_SELECTION = [
        ('draft', 'Draft PO'),
        ('sent', 'RFQ'),
        ('bid', 'Bid Received'),
        ('confirmed', 'Waiting Approval'),
        ('approved', 'Approved'),
        ('except_picking', 'Shipping Exception'),
        ('except_invoice', 'Invoice Exception'),
        ('done', 'Done'),
        ('cancel', 'Cancelled')
    ]

    def _shipped_rate(self, cr, uid, ids, name, arg, context=None):
        if not ids: return {}
        res = {}
        uom_obj = self.pool.get("product.uom")
        for purchase in self.browse(cr, uid, ids, context=context):
            res[purchase.id] = [0.0,0.0]
            for ln in purchase.order_line:
                uom = ln.product_uom
                if not ln.product_id:
                    continue
                if ln.product_id.type == 'service':
                    continue
                qty = uom_obj._compute_qty_obj(cr, uid, uom, ln.product_qty, ln.product_id.uom_id, context=context)
                res[purchase.id][1] += qty or 0.0
       
        cr.execute('''SELECT
                p.order_id, sum(m.product_qty), m.state
            FROM
                stock_move m
            LEFT JOIN
                purchase_order_line p on (p.id=m.purchase_line_id)
            WHERE
                p.order_id IN %s GROUP BY m.state, p.order_id''',(tuple(ids),))
        for oid,nbr,state in cr.fetchall():
            if state=='cancel':
                continue
            if state=='done':
                res[oid][0] += nbr or 0.0

        for r in res:
            if not res[r][1]:
                res[r] = 0.0
            else:
                res[r] = 100.0 * res[r][0] / res[r][1]
        return res
    
    def _all_services(self, cr, uid, ids, name, arg, context=None):
        if not ids: return {}
        res = {}
        uom_obj = self.pool.get("product.uom")
        for purchase in self.browse(cr, uid, ids, context=context):
            
            res[purchase.id] = True
            
            for ln in purchase.order_line:
                if not ln.product_id:
                    continue
                if ln.product_id.type == 'service':
                    continue
                res[purchase.id] = False
        return res

    _columns = {
        'all_service': fields.function(_all_services, string='All Services', type='boolean'),
        'shipped_rate': fields.function(_shipped_rate, string='Received Ratio', type='float'),
        'invoice_method': fields.selection([('order','Based on generated draft invoice'),('picking','Based on incoming shipments')], 'Invoicing Control', required=True,
                    readonly=True, states={'draft':[('readonly',False)], 'sent':[('readonly',False)]},
                    help="Based on Purchase Order lines: place individual lines in 'Invoice Control / On Purchase Order lines' from where you can selectively create an invoice.\n" \
                        "Based on generated invoice: create a draft invoice you can validate later.\n" \
                        "Based on incoming shipments: let you create an invoice when receipts are validated."
                ),
        'state': fields.selection(STATE_SELECTION, 'Status', readonly=True,
                                  help="The status of the purchase order or the quotation request. "
                                       "A request for quotation is a purchase order in a 'Draft' status. "
                                       "Then the order has to be confirmed by the user, the status switch "
                                       "to 'Confirmed'. Then the supplier must confirm the order to change "
                                       "the status to 'Approved'. When the purchase order is paid and "
                                       "received, the status becomes 'Done'. If a cancel action occurs in "
                                       "the invoice or in the receipt of goods, the status becomes "
                                       "in exception.",
                                  select=True, copy=False),
    }

    def view_picking(self, cr, uid, ids, context=None):
        '''
        This function returns an action that display existing picking orders of given purchase order ids.
        '''
        if context is None:
            context = {}
        mod_obj = self.pool.get('ir.model.data')
        dummy, action_id = tuple(mod_obj.get_object_reference(cr, uid, 'stock', 'action_picking_tree'))
        action = self.pool.get('ir.actions.act_window').read(cr, uid, action_id, context=context)

        pick_ids = []
        for po in self.browse(cr, uid, ids, context=context):
#             for picking in po.picking_ids:
#                 if picking.state != 'cancel':
#                     pick_ids.append(picking.id)
             pick_ids += [picking.id for picking in po.picking_ids]

        #override the context to get rid of the default filtering on picking type
        action['context'] = {}
        #choose the view_mode accordingly
        if len(pick_ids) > 1:
            action['domain'] = "[('id','in',[" + ','.join(map(str, pick_ids)) + "])]"
        else:
            res = mod_obj.get_object_reference(cr, uid, 'stock', 'view_picking_form')
            action['views'] = [(res and res[1] or False, 'form')]
            action['res_id'] = pick_ids and pick_ids[0] or False
        return action

    def recreate_picking(self, cr, uid, ids, context=None):
        for purchase in self.browse(cr, uid, ids, context=context):
            data_process = False
            for lines in purchase.order_line:
                if (lines.product_qty - lines.qty_receipt) > 0:
                    data_process = True
                    continue
            if data_process == False:
                return True
#             for pick in purchase.picking_ids:
#                 if pick.state not in ('draft','cancel'):
#                     raise osv.except_osv(
#                         _('Unable to recreate Incoming Shipments.'),
#                         _('Found Active Incoming Shipments For This Purchase Order.'))
#             
            picking_ids = self.action_picking_create(cr, uid, ids, context=context)
        return True

    def action_picking_create(self, cr, uid, ids, context=None):
        for order in self.browse(cr, uid, ids):
            picking_vals = {
                'allow_create': True,
                'picking_type_id': order.picking_type_id.id,
                'partner_id': order.dest_address_id.id or order.partner_id.id,
                'date': max([l.date_planned for l in order.order_line]),
                'origin': order.name
            }
            picking_id = self.pool.get('stock.picking').create(cr, uid, picking_vals, context=context)
            self._create_stock_moves(cr, uid, order, order.order_line, picking_id, context=context)

    def _prepare_order_line_move(self, cr, uid, order, order_line, picking_id, group_id, context=None):
        ''' prepare the stock move data from the PO line. This function returns a list of dictionary ready to be used in stock.move's create()'''
        product_uom = self.pool.get('product.uom')
        price_unit = order_line.price_unit
        if order_line.product_uom.id != order_line.product_id.uom_id.id:
            price_unit *= order_line.product_uom.factor / order_line.product_id.uom_id.factor
        if order.currency_id.id != order.company_id.currency_id.id:
            #we don't round the price_unit, as we may want to store the standard price with more digits than allowed by the currency
            price_unit = self.pool.get('res.currency').compute(cr, uid, order.currency_id.id, order.company_id.currency_id.id, price_unit, round=False, context=context)
        res = []
        move_template = {
            'name': order_line.name or '',
            'product_id': order_line.product_id.id,
            'product_uom': order_line.product_uom.id,
            'product_uos': order_line.product_uom.id,
            'date': order.date_order,
            'date_expected': fields.date.date_to_datetime(self, cr, uid, order_line.date_planned, context),
            'location_id': order.partner_id.property_stock_supplier.id,
            'location_dest_id': order.location_id.id,
            'picking_id': picking_id,
            'partner_id': order.dest_address_id.id,
            'move_dest_id': False,
            'state': 'draft',
            'purchase_line_id': order_line.id,
            'company_id': order.company_id.id,
            'price_unit': price_unit,
            'picking_type_id': order.picking_type_id.id,
            'group_id': group_id,
            'procurement_id': False,
            'origin': order.name,
            'route_ids': order.picking_type_id.warehouse_id and [(6, 0, [x.id for x in order.picking_type_id.warehouse_id.route_ids])] or [],
            'warehouse_id':order.picking_type_id.warehouse_id.id,
            'invoice_state': order.invoice_method == 'picking' and '2binvoiced' or 'none',
        }

        diff_quantity = (order_line.product_qty - order_line.qty_receipt)
        for procurement in order_line.procurement_ids:
            procurement_qty = product_uom._compute_qty(cr, uid, procurement.product_uom.id, procurement.product_qty, to_uom_id=order_line.product_uom.id)
            tmp = move_template.copy()
            tmp.update({
                'product_uom_qty': min(procurement_qty, diff_quantity),
                'product_uos_qty': min(procurement_qty, diff_quantity),
                'move_dest_id': procurement.move_dest_id.id,  #move destination is same as procurement destination
                'group_id': procurement.group_id.id or group_id,  #move group is same as group of procurements if it exists, otherwise take another group
                'procurement_id': procurement.id,
                'invoice_state': procurement.rule_id.invoice_state or (procurement.location_id and procurement.location_id.usage == 'customer' and procurement.invoice_state=='2binvoiced' and '2binvoiced') or (order.invoice_method == 'picking' and '2binvoiced') or 'none', #dropship case takes from sale
                'propagate': procurement.rule_id.propagate,
            })
            diff_quantity -= min(procurement_qty, diff_quantity)
            res.append(tmp)
        #if the order line has a bigger quantity than the procurement it was for (manually changed or minimal quantity), then
        #split the future stock move in two because the route followed may be different.
        if float_compare(diff_quantity, 0.0, precision_rounding=order_line.product_uom.rounding) > 0:
            move_template['product_uom_qty'] = diff_quantity
            move_template['product_uos_qty'] = diff_quantity
            res.append(move_template)
        return res

    def recreate_invoice(self, cr, uid, ids, context=None):
        for purchase in self.browse(cr, uid, ids, context=context):
            for pick in purchase.invoice_ids:
                if pick.state not in ('cancel'):
                    raise osv.except_osv(
                        _('Unable to recreate Supplier Invoices.'),
                        _('Found Active Supplier Invoices For This Purchase Order.'))
            picking_ids = self.action_invoice_create(cr, uid, ids, context=context)
        return True

    def unlink(self, cr, uid, ids, context=None):
        raise osv.except_osv(_('Invalid Action!'), _('cannot delete Purchase Order except of cancel it.'))
        return super(purchase_order, self).unlink(cr, uid, unlink_ids, context=context)

    def action_cancel(self, cr, uid, ids, context=None):
        wf_service = netsvc.LocalService("workflow")
        for purchase in self.browse(cr, uid, ids, context=context):
            for pick in purchase.picking_ids:
                if pick.state not in ('cancel'):
                    raise osv.except_osv(
                        _('Unable to cancel this purchase order.'),
                        _('First cancel all receptions Receipts to this purchase order.'))
            for inv in purchase.invoice_ids:
                if inv and inv.state not in ('cancel'):
                    raise osv.except_osv(
                        _('Unable to cancel this purchase order.'),
                        _('You must first delete or cancel all invoices related to this purchase order.'))
                if inv:
                    wf_service.trg_validate(uid, 'account.invoice', inv.id, 'invoice_cancel', cr)
        self.write(cr,uid,ids,{'state':'cancel'})

purchase_order()

class purchase_order_line(osv.osv):

    _inherit = "purchase.order.line"

    def default_get(self, cr, uid, fields, context=None):
#        raise osv.except_osv(_('Error !'), _('This button still in progress mode'))
        if context is None:
            context = {}
        purchase_order_obj = self.pool.get('purchase.order')
        res = super(purchase_order_line, self).default_get(cr, uid, fields, context=context)
        model = context and 'active_model' in context and context.get(('active_model')) or False
        if model == 'purchase.order':
            for lines in purchase_order_obj.browse(cr, uid, context.get(('active_ids'), []), context=context):
                if not lines.partner_id:
                    raise osv.except_osv(_('Error !'), _('please select the supplier before adding lines'))
                if 'company_id' in fields:
                    res.update({'company_id': lines.company_id.id})
                if 'partner_id_parent' in fields:
                    res.update({'partner_id_parent': lines.partner_id.id})
                if 'pricelist_id_parent' in fields:
                    res.update({'pricelist_id_parent': lines.pricelist_id.id})
                if 'date_order_parent' in fields:
                    res.update({'date_order_parent': lines.date_order})
                if 'fiscal_position_id_parent' in fields:
                    res.update({'fiscal_position_id_parent': lines.fiscal_position.id})
                if 'state_parent' in fields:
                    res.update({'state_parent': lines.state})
                if 'order_id' in fields:
                    res.update({'order_id': lines.id})
        return res


    def revert_invoice(self, cr, uid, ids, context=None):
        account_invoice_obj = self.pool.get('account.invoice')
        for po_line in self.browse(cr, uid, ids, context=context):

            if po_line.invoice_lines:
                for lines in po_line.invoice_lines:
                    if lines.invoice_id.state != 'cancel':
                        raise osv.except_osv(_('Error!'), _('cannot revert to non invoiced until cancel or delete the related Invoiced'))
            self.write(cr, uid, po_line.id, {'invoiced':False}, context=context)
        return True

    def confirm_add_line(self, cr, uid, ids, context=None):
        self.action_confirm(cr, uid, ids, context)
        
#         mod_obj = self.pool.get('ir.model.data')
#         dummy, action_id = tuple(mod_obj.get_object_reference(cr, uid, 'purchase', 'purchase_form_action'))
#         action = self.pool.get('ir.actions.act_window').read(cr, uid, action_id, context=context)
#         res = mod_obj.get_object_reference(cr, uid, 'purchase', 'purchase_order_form')
#         action['views'] = [(res and res[1] or False, 'form')]
#         action['res_id'] = po_id or False
        return {'type': 'ir.actions.act_window_close'}

    def _qty_receipt(self, cr, uid, ids, name, arg, context=None):
        if not ids: return {}
        res = {}
        stock_move_obj = self.pool.get('stock.move')
        for obj in self.browse(cr, uid, ids, context=context):
            qty_delivery = 0.00
            move_ids = stock_move_obj.search(cr, uid, [('purchase_line_id','=',obj.id),('state','!=','cancel')])
            if move_ids:
                for mv in stock_move_obj.browse(cr, uid, move_ids, context=context):

                    qty_delivery = qty_delivery +  mv.product_uom_qty
            res[obj.id] = qty_delivery
        return res

    STATE_SELECTION = [
        ('draft', 'Draft PO'),
        ('sent', 'RFQ'),
        ('bid', 'Bid Received'),
        ('confirmed', 'Waiting Approval'),
        ('approved', 'Done'),
        ('except_picking', 'Shipping Exception'),
        ('except_invoice', 'Invoice Exception'),
        ('done', 'Done'),
        ('cancel', 'Cancelled')
    ]

    _columns = {
        'partner_id_parent': fields.many2one('res.partner', 'Supplier'),
        'fiscal_position_id_parent': fields.many2one('account.fiscal.position', 'Fiscal Position'),
        'pricelist_id_parent': fields.many2one('product.pricelist', 'Pricelist'),
        'date_order_parent': fields.datetime('Date'),
        'state_parent': fields.selection(STATE_SELECTION, 'Status', readonly=True),
        'qty_receipt': fields.function(_qty_receipt, type='float', string='Qty Receipt'),
    }

purchase_order_line()

# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
